package com.youbo.service.impl;

import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sccba.open.oauth2.APIRequest;
import com.sccba.open.oauth2.APIResponse;
import com.sccba.open.oauth2.AccessTokenResponse;
import com.youbo.common.exception.RRException;
import com.youbo.common.utils.*;
import com.youbo.config.ProjectConfig;
import com.youbo.dao.JnOrderDao;
import com.youbo.entity.JnOrderEntity;
import com.youbo.entity.OrderOutEntity;
import com.youbo.form.RequestOrder;
import com.youbo.service.JnOrderService;
import com.youbo.service.OrderOutService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service("jnOrderService")
public class JnOrderServiceImpl extends ServiceImpl<JnOrderDao, JnOrderEntity> implements JnOrderService {

    private final Logger logger = LoggerFactory.getLogger(getClass());


    @Autowired
    OrderOutService orderOutService;
    @Autowired
    ProjectConfig config;



    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class, RRException.class})
    @Override
    public R createOrder(RequestOrder requestOrder) {
        JnOrderEntity order=null;
        try{

            order=getOne(new QueryWrapper<JnOrderEntity>()
                    .eq("sub_no",requestOrder.getOrderNo()));
            if(null!=order){
                return R.ok().put("order",order);
            }

            order=new JnOrderEntity();
            order.setOrderId(IDUtils.getUUId());
            order.setSysSeqno(IDUtils.getUUId());
            order.setOrderTitle(requestOrder.getOrderTitle());
            order.setOrderDescription(requestOrder.getOrder_description());
            order.setAmount(requestOrder.getAmount());
            order.setSubNo(requestOrder.getOrderNo());
            order.setMchntCd((null==requestOrder.getMchntCd())?config.getMchntCd():requestOrder.getMchntCd());
            order.setOrderType(0);
            order.setState(1);
            order.setCreateDate(new Date());
            if(null!=requestOrder.getUserId()){
                order.setUserId(requestOrder.getUserId());
            }

            //logger.debug("SysSeqno:"+order.getSysSeqno()+"  OrderId:"+order.getOrderId());

            APIRequest  request;
            APIResponse response;

            if(StringUtils.isNotBlank(requestOrder.getAuthcode())){
                request=getCreateCodeOrderAPIRequest(order,"801110",requestOrder.getAuthcode());
                response = config.getInstanceOpenAPI().sendRequest(config.getBankId(),"", "/"+config.getAppId()+"/PAY1011", request);
            }else{
                request=getCreateOrderAPIRequest(order,"801101");
                response = config.getInstanceOpenAPI().sendRequest(config.getBankId(),"", "/"+config.getAppId()+"/PAY1012", request);
            }

            String code =response.getResponseCode();
            if(code.equals("invalid_token")){
                resetAccessToken(config.getAppId());
                return createOrder(requestOrder);
            }

            if(!code.equals("S000A000")){
                logger.error("城商联盟接口错误:"+response.getResponseDesc());
                throw new RRException("城商联盟接口错误:"+response.getResponseDesc());
            }
            Map data= JsonTools.string2Obj(response.getResponseData(), Map.class);
            if(null==data){
                logger.error("城商联盟接口错误:"+":"+response.getResponseData());
                throw new RRException("城商联盟接口错误:"+":"+response.getResponseData());
            }

            String result_code= Tools.toString(data.get("result_code"));

            if(!result_code.equals("000000")&&!result_code.equals("000001")){//"result_code" -> "000001"
                logger.error(response.getResponseData());
                logger.error("城商联盟接口错误:"+data.get("result_message").toString());
                throw new RRException("城商联盟接口错误:"+data.get("result_message").toString());
            }

            order.setEpayRrn(Tools.toString(data.get("epay_rrn")));
            order.setExpireTime(DateUtils.stringToDate(Tools.toString(data.get("expire_time")),"yyyyMMddHHmmss"));
            order.setCodeUrl(Tools.toString(data.get("code_url")));
            order.setApplyIss(Tools.toString(data.get("apply_iss")));

            if(StringUtils.isNotBlank(requestOrder.getAuthcode())){
                String payer_info=Tools.toString(data.get("payer_info"));
                String payer_channel=Tools.toString(data.get("payer_channel"));
                String payer_datetime=Tools.toString(data.get("trans_datetime"));
                order.setState(2);
                order.setPayDate(DateUtils.stringToDate(payer_datetime,"yyyyMMddHHmmss"));
                order.setPayerChannel(payer_channel);
                order.setPayerInfo(payer_info);
            }


            if(!save(order)){
                throw new RRException("订单保存失败");
            }

        }catch (Exception e){
            throw new RRException(e.getMessage());
        }
        return R.ok().put("order",order);
    }


    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class,RRException.class})
    @Override
    public R queryOrder(RequestOrder requestOrder) {
        JnOrderEntity order=null;
        try{
            order=getOne(new QueryWrapper<JnOrderEntity>().eq("sub_no",requestOrder.getOrderNo()));
            if(null==order){
                throw new RRException("订单不存在");
            }
            //1=等待支付 2=支付成功 3=退款 4=取消 5=其他',
            if(order.getState()==2){
                return R.ok().put("order",order);
            }
            if(order.getState()==3){
                throw new RRException("订单已退款");
            }
            if(order.getState()==4){
                throw new RRException("订单已超时取消");
            }

            APIRequest  request=getQueryOrderAPIRequest(order,"801014");
            APIResponse response = config.getInstanceOpenAPI().sendRequest(config.getBankId(),"", "/"+config.getAppId()+"/PAY1018", request);
            String code =response.getResponseCode();


            if(code.equals("invalid_token")){
                resetAccessToken(config.getAppId());
                return queryOrder(requestOrder);
            }

            if(!code.equals("S000A000")){
                logger.error("城商联盟接口错误:"+response.getResponseDesc());
                throw new RRException("城商联盟接口错误:"+response.getResponseDesc());
            }
            Map data=JsonTools.string2Obj(response.getResponseData(), Map.class);
            if(null==data){
                logger.error("城商联盟接口错误:"+":"+response.getResponseData());
                throw new RRException("城商联盟接口错误:"+":"+response.getResponseData());
            }

            //String epay_rrn=Tools.toString("epay_rrn");
            String payer_info=Tools.toString(data.get("payer_info"));
            String payer_channel=Tools.toString(data.get("payer_channel"));
            String payer_datetime=Tools.toString(data.get("payer_datetime"));
            //String payer_payamt=Tools.toString("payer_payamt");
            String trans_state=Tools.toString(data.get("trans_state"));//0-成功 1-失败 8-已撤销 9-处理中

            if(null==trans_state){
                throw new RRException("返回支付状态失败");
            }

            if(trans_state.equals("0")){//支付成功
                order.setState(2);
                order.setPayDate(DateUtils.stringToDate(payer_datetime,"yyyyMMddHHmmss"));
                order.setPayerChannel(payer_channel);
                order.setPayerInfo(payer_info);
                if(!updateById(order)){
                    throw new RRException("保存支付结果失败");
                }

                return R.ok().put("order",order);

            }else if(trans_state.equals("1")){
                throw new RRException("支付失败");
            }else if(trans_state.equals("8")){
                throw new RRException("支付已撤销");
            }else if(trans_state.equals("9")){
                throw new RRException("支付处理中");
            }else {
                throw new RRException("未知支付状态");
            }

        }catch (Exception e){
            throw new RRException(e.getMessage());
        }
    }
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class,RRException.class})
    @Override
    public R outOrder(RequestOrder requestOrder) {
        JnOrderEntity order=null;
        try{
            order=getOne(new QueryWrapper<JnOrderEntity>().eq("sub_no",requestOrder.getOrderNo()));
            if(null==order){
                throw new RRException("订单不存在");
            }
            //1=等待支付 2=支付成功 3=退款 4=取消 5=其他',
            if(order.getState()==1){
                throw new RRException("订单还未支付");
            }
            if(order.getState()==3){
                List<OrderOutEntity> list=orderOutService.list(new QueryWrapper<OrderOutEntity>().eq("order_id",order.getOrderId()));
                double mo=0;
                for (OrderOutEntity orderOut:list) {
                    mo+=orderOut.getAmount();
                }
                if(mo>=order.getAmount()){
                    throw new RRException("订单已退款");
                }
                if((mo+requestOrder.getAmount())>order.getAmount()){
                    throw new RRException("退款金额不能超出订单金额");
                }
            }
            if(order.getState()==4){
                throw new RRException("订单已超时取消");
            }


            OrderOutEntity out=new OrderOutEntity();
            out.setAmount(requestOrder.getAmount());
            out.setOrderId(order.getOrderId());
            out.setCreateDate(new Date());
            out.setSysSeqno(Tools.getOrderIdByUUId2());


            APIRequest  request=getOutOrderAPIRequest(out,"801021");
            APIResponse response = config.getInstanceOpenAPI().sendRequest(config.getBankId(),"", "/"+config.getAppId()+"/PAY1020", request);
            String code =response.getResponseCode();

            //logger.debug("ResponseData:"+response.getResponseData());
            if(code.equals("invalid_token")){
                resetAccessToken(config.getAppId());
                return outOrder(requestOrder);
            }
            if(!code.equals("S000A000")){
                logger.error("城商联盟接口错误:"+response.getResponseDesc());
                throw new RRException("城商联盟接口错误:"+response.getResponseDesc());
            }
            Map data=JsonTools.string2Obj(response.getResponseData(), Map.class);
            if(null==data){
                logger.error("城商联盟接口错误:"+":"+response.getResponseData());
                throw new RRException("城商联盟接口错误:"+":"+response.getResponseData());
            }

            String result_code=Tools.toString(data.get("result_code"));
            String result_message=Tools.toString(data.get("result_message"));
            String epay_rrn=Tools.toString(data.get("epay_rrn"));

            if(null==result_code){
                throw new RRException("返回支付状态失败");
            }

            if(result_code.equals("000000")){//退款成功
                out.setEpayRrn(epay_rrn);
                out.setResultCode(result_code);
                out.setResultMessage(result_message);
                order.setState(3);
                updateById(order);
                orderOutService.save(out);
                return R.ok().put("order",out);
            }else {
                throw new RRException(result_message);
            }

        }catch (Exception e){
            throw new RRException(e.getMessage());
        }
    }


    /**
     * 根据订单生成 报文
     * @param order
     * @return
     * @throws Exception
     */
    private APIRequest getQueryOrderAPIRequest(JnOrderEntity order, String bizKey) throws Exception {
        APIRequest request = new APIRequest();
        Map<String, Object> dataMap = new HashMap<String, Object>();

        dataMap.put("yybs",config.getYybs());
        dataMap.put("cpdm",config.getCpdm());
        dataMap.put("jydm",bizKey);
        dataMap.put("sys_seqno",Tools.getOrderIdByUUId2());
       // dataMap.put("channel_trace_no",order.getSysSeqno());
        dataMap.put("merch_order_no",order.getOrderId());
        dataMap.put("mchnt_cd",order.getMchntCd());
        dataMap.put("mchnt_mcc","8062");//公立医院  8062 

        String[] signKeyMap=new String[]{"yybs","cpdm","jydm","sys_seqno","merch_order_no","mchnt_cd"};
        String sign="";
        for (String key:signKeyMap) {
            sign+=(sign.equals(""))?key+"="+dataMap.get(key):"&"+key+"="+dataMap.get(key);
        }
        sign+="&sign_key="+config.getSecretKey();
        sign=Tools.md5(sign).toUpperCase();
        dataMap.put("sign",sign);

        String json = JSON.toJSONString(dataMap);

        request.setData(json);//json报文
        request.setBkId(config.getBankId());
        request.setBizKey(bizKey);

        request.setNonceStr(String.valueOf((new Random()).nextInt()));
        request.setTimestamp(DateUtils.format(new Date(),"yyyyMMddHHmmssSSSS"));//18位时间戳yyyyMMddHHmmssSSSS
        String accessToken=getAccessToken(config.getAppId());
        request.setAccessToken(accessToken);
        return request;
    }

    /**
     * 根据code订单生成 报文
     * @param order
     * @return
     * @throws Exception
     */
    private APIRequest getCreateCodeOrderAPIRequest(JnOrderEntity order, String bizKey, String payer_authcode) throws Exception {
        APIRequest request = new APIRequest();
        Map<String, Object> dataMap = new HashMap<String, Object>();
        String amt_currency="CNY";
        String trans_datetime=DateUtils.format(new Date(),"yyyyMMddHHmmss");

        dataMap.put("yybs",config.getYybs());
        dataMap.put("cpdm",config.getCpdm());
        dataMap.put("jydm",bizKey);
        dataMap.put("sys_seqno",order.getSysSeqno());
        dataMap.put("order_id",order.getOrderId());
        dataMap.put("order_title",order.getOrderTitle());
        dataMap.put("order_description",order.getOrderDescription());
        dataMap.put("amount", order.getAmount());
        dataMap.put("amt_currency",amt_currency);
        dataMap.put("trans_datetime",trans_datetime);//yyyyMMddHHmmss
        dataMap.put("payer_authcode",payer_authcode);
        dataMap.put("mchnt_cd",order.getMchntCd());
        dataMap.put("mchnt_mcc","8062");//公立医院  8062 
        dataMap.put("order_life",config.getOrderLife());
                                          //yybs|cpdm|jydm|sys_seqno|order_id|amount|amt_currency|trans_datetime|payer_authcode|mchnt_cd|order_life
        String[] signKeyMap=new String[]{"yybs","cpdm","jydm","sys_seqno","order_id","amount","amt_currency","trans_datetime","payer_authcode","mchnt_cd"};
        String sign="";
        for (String key:signKeyMap) {
            sign+=(sign.equals(""))?key+"="+dataMap.get(key):"&"+key+"="+dataMap.get(key);
        }
        sign+="&sign_key="+config.getSecretKey();
        //logger.debug("签名前:"+sign);
        sign=Tools.md5(sign).toUpperCase();
        //logger.debug("签名后:"+sign);

        dataMap.put("sign",sign);
        String json = JSON.toJSONString(dataMap);

        request.setData(json);//json报文
        request.setBkId(config.getBankId());
        request.setBizKey(bizKey);

        request.setNonceStr(String.valueOf((new Random()).nextInt()));
        request.setTimestamp(DateUtils.format(new Date(),"yyyyMMddHHmmssSSSS"));//18位时间戳yyyyMMddHHmmssSSSS
        String accessToken=getAccessToken(config.getAppId());
        request.setAccessToken(accessToken);
        return request;
    }

    /**
     * 根据订单生成 报文
     * @param order
     * @return
     * @throws Exception
     */
    private APIRequest getCreateOrderAPIRequest(JnOrderEntity order, String bizKey) throws Exception {

//        for (String key:signKeyMap) {
//            sign+=(sign.equals(""))?dataMap.get(key):"&"+dataMap.get(key);
//        }

        APIRequest request = new APIRequest();
        Map<String, Object> dataMap = new HashMap<String, Object>();
        String amt_currency="CNY";
        String trans_datetime=DateUtils.format(new Date(),"yyyyMMddHHmmss");

        dataMap.put("yybs",config.getYybs());
        dataMap.put("cpdm",config.getCpdm());
        dataMap.put("jydm",bizKey);
        dataMap.put("sys_seqno",order.getSysSeqno());
        dataMap.put("order_id",order.getOrderId());
        dataMap.put("order_title",order.getOrderTitle());
        dataMap.put("order_description",order.getOrderDescription());
        dataMap.put("order_life",config.getOrderLife());//10分钟
        dataMap.put("amt_source","00");
        dataMap.put("amount", order.getAmount());
        dataMap.put("amt_currency",amt_currency);
        dataMap.put("trans_datetime",trans_datetime);//yyyyMMddHHmmss
        dataMap.put("mchnt_cd",order.getMchntCd());
        dataMap.put("mchnt_mcc","8062");//公立医院  8062 
        if(StringUtils.isNotBlank(config.getNotifyUrl())){
            dataMap.put("notify_url",config.getNotifyUrl());
        }
        //签名顺序

        String[] signKeyMap=new String[]{"yybs","cpdm","jydm","sys_seqno","order_id","order_life","amt_source","amount","amt_currency","trans_datetime","mchnt_cd"};
        String sign="";
        for (String key:signKeyMap) {
            sign+=(sign.equals(""))?key+"="+dataMap.get(key):"&"+key+"="+dataMap.get(key);
        }
        sign+="&sign_key="+config.getSecretKey();

        logger.debug("签名前:"+sign);
        sign=Tools.md5(sign).toUpperCase();
        logger.debug("签名后:"+sign);

        dataMap.put("sign",sign);
        String json = JSON.toJSONString(dataMap);

        request.setData(json);//json报文
        request.setBkId(config.getBankId());
        request.setBizKey(bizKey);

        request.setNonceStr(String.valueOf((new Random()).nextInt()));
        request.setTimestamp(DateUtils.format(new Date(),"yyyyMMddHHmmssSSSS"));//18位时间戳yyyyMMddHHmmssSSSS
        String accessToken=getAccessToken(config.getAppId());
        request.setAccessToken(accessToken);
        return request;
    }




    /**
     * 根据订单 退款
     * @param outOrder
     * @return
     * @throws Exception
     */
    private APIRequest getOutOrderAPIRequest(OrderOutEntity outOrder, String bizKey) throws Exception {
        APIRequest request = new APIRequest();
        Map<String, Object> dataMap = new HashMap<String, Object>();

        JnOrderEntity order=getById(outOrder.getOrderId());
        if(null==order){
            throw  new RRException("退款订单不存在");
        }

        dataMap.put("yybs",config.getYybs());
        dataMap.put("cpdm",config.getCpdm());
        dataMap.put("jydm",bizKey);
        dataMap.put("sys_seqno",outOrder.getSysSeqno());
        dataMap.put("ori_sys_seqno",order.getSysSeqno());
        dataMap.put("ori_order_id",order.getOrderId());
        dataMap.put("mchnt_cd",config.getMchntCd());
        dataMap.put("mchnt_mcc","8062");//公立医院  8062 
        dataMap.put("refound_amt",outOrder.getAmount());//退款金额

        String[] signKeyMap=new String[]{"yybs","cpdm","jydm","sys_seqno","ori_sys_seqno","ori_order_id","mchnt_cd","refound_amt"};
        String sign="";
        for (String key:signKeyMap) {
            sign+=(sign.equals(""))?key+"="+dataMap.get(key):"&"+key+"="+dataMap.get(key);
        }
        sign+="&sign_key="+config.getSecretKey();
        sign=Tools.md5(sign).toUpperCase();

        dataMap.put("sign",sign);

        String json = JSON.toJSONString(dataMap);

        request.setData(json);//json报文
        request.setBkId(config.getBankId());
        request.setBizKey(bizKey);

        request.setNonceStr(String.valueOf((new Random()).nextInt()));
        request.setTimestamp(DateUtils.format(new Date(),"yyyyMMddHHmmssSSSS"));//18位时间戳yyyyMMddHHmmssSSSS
        String accessToken=getAccessToken(config.getAppId());
        request.setAccessToken(accessToken);
        return request;
    }




    public String getAccessToken(String appId){
        if(null==ProjectConfig.ACCESS_TOKEN||null==ProjectConfig.ACCESS_EXPIRESDATE){
            resetAccessToken(appId);
        }
        if (ProjectConfig.ACCESS_EXPIRESDATE.before(new Date())) {// token 过期
            resetAccessToken(appId);
        }
        return ProjectConfig.ACCESS_TOKEN;
    }

    public void resetAccessToken(String appId){
        ProjectConfig.ACCESS_TOKEN=null;
        ProjectConfig.REFRESH_TOKEN=null;

        AccessTokenResponse res=getAccessWebToken();
        if(null!=res){
            ProjectConfig.ACCESS_TOKEN=res.getAccessToken();
            ProjectConfig.ACCESS_EXPIRESDATE=DateUtils.stringToDate(res.getTokenExpiresDate(), "yyyy-MM-dd HH:mm:ss");
        }

    }

    /**
     * 根据网络获取 token
     * @return
     */
    public AccessTokenResponse getAccessWebToken() {
        AccessTokenResponse response=new AccessTokenResponse();
        String resStr=HttpUtils.sendGet(config.getTokenUrl(),null);

        if(null==resStr){
            return null;
        }
        Map<String,Object> res=(Map<String,Object>) JSONUtils.parse(resStr);
        if(null==resStr){
            return null;
        }
        int code= Tools.toInt(res.get("code"));
        if(code!=0){
            return null;
        }
        String expiresDate=Tools.toString(res.get("expiresDate"));
        response.setAccessToken(Tools.toString(res.get("token")));
        response.setTokenExpiresDate(expiresDate);
        return response;
    }


}
